package com.ytangdata.test.rest;

import java.io.Serializable;
import java.util.Collection;
import java.util.Map;
import java.util.Set;

import javax.persistence.EntityManager;
import javax.transaction.Transactional;

import org.hibernate.Query;
import org.hibernate.Session;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;

import edu.fudan.mylang.pf.BaseModelObject;
import edu.fudan.mylang.pf.EntityImplementation;
import edu.fudan.mylang.pf.GenericQueryImpl;
import edu.fudan.mylang.pf.IContextAwareModelObject;
import edu.fudan.mylang.pf.IModelObject;
import edu.fudan.mylang.pf.IObjectFactory;
import edu.fudan.mylang.pf.IParameterizedQuery;
import edu.fudan.mylang.pf.IPersistenceEventListener;
import edu.fudan.mylang.pf.MapQueryImpl;
import edu.fudan.mylang.pf.PersistenceException;
import edu.fudan.mylang.pf.query.IQueryCriteria;

@Transactional
public class ObjectFactory implements IObjectFactory, BeanFactoryAware{
	
	private EntityManager entityManager;
	private BeanFactory beanFactory;
	
	public void setEntityManager(EntityManager entityManager) {
		this.entityManager = entityManager;
	}

	@Override
	public void addPersistenceEventListeners(IPersistenceEventListener obj) {
		throw new RuntimeException("not supported");
	}

	@Override
	public <T extends IModelObject> Collection<T> all(Class<T> obj) {
		throw new RuntimeException("not supported");
	}

	@Override
	public <T extends IModelObject> Collection<T> allWithDeactived(Class<T> obj) {
		throw new RuntimeException("not supported");
	}

	@Override
	public <T extends IModelObject> T create(Class<T> objType) {
		EntityImplementation entityImplAnnotation = objType
				.getAnnotation(EntityImplementation.class);
		Class<? extends IModelObject> entityType = entityImplAnnotation
				.implementBy();
		try {
			IModelObject entityInstance = entityType.newInstance();
			return (T)create(entityInstance);
		} catch (InstantiationException e) {
			throw new PersistenceException(e);
		} catch (IllegalAccessException e) {
			throw new PersistenceException(e);
		}
	}

	@Override
	public IModelObject create(IModelObject obj) {
		BaseModelObject object = ((BaseModelObject) obj);
		object.preinitialize();
		entityManager.persist(object);
		object.setObjectFactory((IObjectFactory) beanFactory.getBean("objectFactory"));
		object.entityInitialized(beanFactory);
		return object;
	}

	@Override
	public <T> IParameterizedQuery<T> createGenericQuery(Class<T> type, String hql) {
		return new GenericQueryImpl<T>(this, hql);
	}

	@Override
	public IParameterizedQuery<Map<String, Object>> createMapQuery(String hql) {
		return new MapQueryImpl(this,hql);
	}

	@Override
	public Query createQuery(String hql) {
		//TODO
		throw new RuntimeException("not supported");
	}

	@Override
	public <S, T extends IQueryCriteria<S>> T createQueryCriteria(Class<T> arg0) {
		throw new RuntimeException("not supported");
	}

	@Override
	public void delete(IModelObject obj) {
		entityManager.remove(obj);
	}

	@Override
	public void deleteAll(Class<? extends IModelObject> clazz) {
		throw new RuntimeException("not supported");
	}

	@Override
	public <M> Iterable<M> executeQuery(Query arg0) {
		throw new RuntimeException("not supported");
	}

	@Override
	public boolean exist(IModelObject arg0) {
		throw new RuntimeException("not supported");
	}

	@Override
	public <T extends IModelObject> T get(Class<T> clazz, Serializable id) {
		IModelObject bean = (IModelObject) entityManager.getReference(clazz, id);
		return (T) bean;
	}

	@Override
	public BeanFactory getBeanFactory() {
		return this.beanFactory;
	}

	@Override
	public Session getHibernateSession() {
		throw new RuntimeException("not supported");
	}

	@Override
	public Set<IPersistenceEventListener> getPersistenceEventListeners() {
		throw new RuntimeException("not supported");
	}

	@Override
	public IModelObject load(IModelObject obj) {
		return get(obj.getClass(), obj.getId());
	}

	@Override
	public void logicDelete(IModelObject obj) {
		obj.setIsActive(false);
		save(obj);
	}

	@Override
	public void save(IModelObject obj) {
		entityManager.persist(obj);
		if (obj instanceof IContextAwareModelObject) {
			((IContextAwareModelObject) obj).setObjectFactory((IObjectFactory) beanFactory.getBean("objectFactory"));
			((IContextAwareModelObject) obj).entityInitialized(beanFactory);
		}
	}

	@Override
	public void save(IModelObject arg0, Serializable arg1) {
		throw new RuntimeException("not supported");
	}

	@Override
	public void setPersistenceEventListeners(Set<IPersistenceEventListener> arg0) {
		throw new RuntimeException("not supported");
	}

	@Override
	public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
		this.beanFactory = beanFactory;
	}

}
